extern crate chrono;

use std::collections::{HashMap, HashSet};
use std::string::ToString;

use crate::config::app_error::AppResult;
use crate::extension::index::index::query::query::query_factory::{
    and_vec, contains, equal, Query,
};
use crate::extension::index::index::query::query::{query_factory, BaseQuery};
use crate::extension::index::page_request::{Direction, Order, Sort};
use crate::extension::index::router::list_options::ListOptions;
use crate::extension::index::router::list_request::ListRequest;
use crate::extension::index::selector::field_selector::FieldSelector;
use crate::extension::index::selector::selector_util;
use crate::extension::model::role::Role;
use crate::extension::model::user;
use crate::utils::datetime_util::date_time_format;
use chrono::{DateTime, Utc};
use macros::gvk;
use query_factory::or;
use salvo::prelude::{Extractible, ToSchema};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug, ToSchema, Default)]
pub struct UserAddRequest {
    pub username: String,
    pub password: String,
}

#[derive(Debug, Deserialize, ToSchema, Default)]
pub struct UserLoginRequest {
    pub username: String,
    pub password: String,
}

#[derive(Debug, Deserialize, Extractible, ToSchema, Default)]
#[salvo(extract(default_source(from = "body", format = "json")))]
pub struct UserUpdateRequest {
    #[salvo(extract(source(from = "param")))]
    pub id: String,
    pub username: String,
    pub password: String,
}

#[derive(Debug, Serialize, ToSchema, Default)]
pub struct UserResponse {
    pub id: String,
    pub username: String,
}

#[derive(Debug, Serialize, ToSchema, Default)]
pub struct UserLoginResponse {
    pub id: String,
    pub username: String,
    pub token: String,
    pub exp: usize,
}

#[derive(Debug, Serialize, Default)]
pub struct DetailedUser {
    pub user: User,
    pub roles: Vec<Role>,
}

/// 登录用户信息
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LoginUserInfo {
    pub username: String,
    pub roles: HashSet<String>,
    pub token: String,
    pub exp: usize,
}


pub const ROLE_NAMES_ANNO: &str = "rbac.authorization.halo.run/role-names";
pub const LAST_AVATAR_ATTACHMENT_NAME_ANNO: &str = "halo.run/last-avatar-attachment-name";
pub const AVATAR_ATTACHMENT_NAME_ANNO: &str = "halo.run/avatar-attachment-name";
pub const EMAIL_TO_VERIFY: &str = "halo.run/email-to-verify";
pub const USER_RELATED_ROLES_INDEX: &str = "roles";

#[gvk(
    group = "",
    version = "v1alpha1",
    kind = "User",
    singular = "user",
    plural = "users"
)]
pub struct User {
    #[serde(default)]
    pub spec: UserSpec,
    #[serde(default)]
    pub status: UserStatus,
}

impl User {
    pub fn new_with(spec: UserSpec, status: UserStatus) -> Self {
        let mut user = User::new();
        user.spec = spec;
        user.status = status;
        user
    }
}

impl std::hash::Hash for User {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.metadata.hash(state);
        self.spec.hash(state);
        self.status.hash(state);
    }
}



#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, Default, Hash, PartialEq, Eq)]
pub struct UserSpec {
    #[serde(rename = "displayName")]
    pub display_name: String,

    #[serde(rename = "avatar")]
    pub avatar: Option<String>,

    #[serde(rename = "email")]
    pub email: String,

    #[serde(rename = "emailVerified")]
    pub email_verified: Option<bool>,

    #[serde(rename = "phone")]
    pub phone: Option<String>,

    #[serde(rename = "password")]
    pub password: Option<String>,

    #[serde(rename = "bio")]
    pub bio: Option<String>,

    #[serde(default)]
    #[serde(with = "date_time_format", rename = "registeredAt")]
    pub registered_at: Option<DateTime<Utc>>,

    #[serde(rename = "twoFactorAuthEnabled")]
    pub two_factor_auth_enabled: Option<bool>,

    #[serde(
        rename = "totpEncryptedSecret",
        skip_serializing_if = "Option::is_none"
    )]
    pub totp_encrypted_secret: Option<String>,

    #[serde(rename = "disabled", skip_serializing_if = "Option::is_none")]
    pub disabled: Option<bool>,

    #[serde(rename = "loginHistoryLimit", skip_serializing_if = "Option::is_none")]
    pub login_history_limit: Option<i32>,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default, Hash, PartialEq, Eq)]
pub struct UserStatus {
    #[serde(default)]
    #[serde(with = "date_time_format", rename = "lastLoginAt")]
    pub last_login_at: Option<DateTime<Utc>>,

    #[serde(rename = "permalink")]
    pub permalink: Option<String>,

    #[serde(rename = "loginHistories")]
    pub login_histories: Option<Vec<LoginHistory>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Default, Hash, PartialEq, Eq)]
pub struct LoginHistory {
    #[serde(default)]
    #[serde(with = "date_time_format", rename = "loginAt")]
    pub login_at: Option<DateTime<Utc>>,
    #[serde(rename = "sourceIp")]
    pub source_ip: String,
    #[serde(rename = "userAgent")]
    pub user_agent: String,
    #[serde(rename = "successful")]
    pub successful: bool,
    #[serde(rename = "reason")]
    pub reason: Option<String>,
}

/// 创建用户
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct CreateUserRequest {
    #[serde(rename = "name")]
    pub name: String,

    #[serde(rename = "email")]
    pub email: String,

    #[serde(rename = "displayName")]
    pub display_name: Option<String>,

    #[serde(rename = "avatar")]
    pub avatar: Option<String>,

    #[serde(rename = "phone")]
    pub phone: Option<String>,

    #[serde(rename = "password")]
    pub password: Option<String>,

    #[serde(rename = "bio")]
    pub bio: Option<String>,

    #[serde(rename = "annotations")]
    pub annotations: Option<HashMap<String, String>>,

    #[serde(rename = "roles")]
    pub roles: Option<HashSet<String>>,
}

impl CreateUserRequest {
    pub fn to_user(&self) -> User {
        let spec = UserSpec {
            display_name: self.display_name.clone().unwrap_or(self.name.clone()),
            avatar: self.avatar.clone(),
            email: self.email.clone(),
            phone: self.phone.clone(),
            bio: self.bio.clone(),
            ..Default::default()
        };
        let user = User::new_with(spec, UserStatus::default());
        user
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct UserPermission {
    #[serde(rename = "roles")]
    pub roles: HashSet<Role>,

    #[serde(rename = "permissions")]
    pub permissions: Vec<Role>,

    #[serde(rename = "uiPermissions")]
    pub ui_permissions: HashSet<String>,
}

#[derive(Debug, Serialize, Deserialize, Extractible, Clone, Default)]
#[salvo(extract(default_source(from = "query")))]
pub struct UserListRequest {
    pub page: usize,

    pub size: usize,

    #[serde(rename = "fieldSelector")]
    pub label_selector: Option<Vec<String>>,

    #[serde(rename = "fieldSelector")]
    pub field_selector: Option<Vec<String>>,

    pub keyword: Option<String>,

    pub role: Option<String>,

    pub sort: Option<Vec<String>>,
}

impl UserListRequest {
    pub fn new() -> Self {
        UserListRequest {
            page: 0,
            size: 0,
            label_selector: None,
            field_selector: None,
            keyword: None,
            role: None,
            sort: None,
        }
    }
}

impl ListRequest for UserListRequest {
    fn get_page(&self) -> usize {
        self.page
    }

    fn get_size(&self) -> usize {
        self.size
    }

    fn get_label_selector(&self) -> Vec<String> {
        self.label_selector.clone().unwrap_or(vec![])
    }

    fn get_field_selector(&self) -> Vec<String> {
        self.field_selector.clone().unwrap_or(vec![])
    }
}

impl UserListRequest {
    pub fn get_sort(&self) -> Sort {
        let sort = self.sort.clone().unwrap_or(Vec::new());
        let mut order_vec = Vec::new();
        for sort_str in sort {
            let vec = sort_str.split(",").collect::<Vec<_>>();
            let field = vec[0];
            let order_str = vec[1];
            let order = Order::new(Direction::from_string(order_str), field.to_string());
            order_vec.push(order);
        }
        Sort::new(order_vec)
    }

    pub fn get_keyword(&self) -> String {
        self.keyword.clone().unwrap_or("".to_string())
    }
    pub fn get_role(&self) -> String {
        self.role.clone().unwrap_or("".to_string())
    }

    pub fn to_list_options(&self) -> AppResult<ListOptions> {
        let labels = self.get_label_selector();
        let fields = self.get_field_selector();

        let list_options =
            selector_util::label_and_field_selector_to_list_options::<User>(labels, fields)?;

        let field_query = list_options.field_selector.query;
        let mut query_list = Vec::new();
        let keyword = self.get_keyword();
        if !keyword.is_empty() {
            let query = Query::Or(or(
                Query::StringContains(contains("spec.displayName", keyword.clone())),
                Query::Equal(equal("metadata.name", keyword.clone())),
            ));
            query_list.push(query);
        }
        let role = self.get_role();
        if !role.is_empty() {
            let query = Query::Equal(equal(user::USER_RELATED_ROLES_INDEX, role));
            query_list.push(query);
        }
        query_list.push(field_query);
        let query = and_vec(query_list)?;
        let field_selector = FieldSelector::of(query);
        let list_options = ListOptions::new(list_options.label_selector, field_selector);

        Ok(list_options)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema)]
pub struct ChangePasswordRequest {
    pub password: String,
}

#[allow(unused_imports)]
mod tests {
    use crate::extension::model::user::User;

    #[tokio::test]
    async fn test_hello_world() {
        let data = b"{\"spec\":{\"displayName\":\"Anonymous User\",\"email\":\"anonymous@example.com\",\"emailVerified\":false,\"disabled\":true},\"status\":{},\"apiVersion\":\"v1alpha1\",\"kind\":\"User\",\"metadata\":{\"finalizers\":[\"user-protection\",\"system-protection\"],\"name\":\"anonymousUser\",\"labels\":{\"halo.run/hidden-user\":\"true\"},\"annotations\":{\"rbac.authorization.halo.run/role-names\":\"[]\"},\"version\":3,\"creationTimestamp\":\"2023-04-25T05:46:06.870656600Z\"}}";
        let user: User = serde_json::from_slice(data).unwrap();
        println!("{:?}", user);
    }

    // #[tokio::test]
    // async fn test_hello_world2() {
    //     let data = b"{"name":"/registry/users/anonymousUser","data":"{\"spec\":{\"displayName\":\"Anonymous User\",\"email\":\"anonymous@example.com\",\"emailVerified\":false,\"disabled\":true},\"status\":{},\"apiVersion\":\"v1alpha1\",\"kind\":\"User\",\"metadata\":{\"finalizers\":[\"user-protection\",\"system-protection\"],\"name\":\"anonymousUser\",\"labels\":{\"halo.run/hidden-user\":\"true\"},\"annotations\":{\"rbac.authorization.halo.run/role-names\":\"[]\"},\"version\":3,\"creationTimestamp\":\"2023-04-25T05:46:06.870656600Z\"}}","version":"4"}";
    //     let user: User = serde_json::from_slice(data).unwrap();
    //     println!("{:?}", user);
    // }
}
